/**
* 
* GUI: Gant graph window  
* Copyright (c) 2009 Ecole Centrale de Nantes IRCCyN  
* Classe: graphgantt.java 
* see <a href="{@docRoot}/">Trace for more information</a>
* 
* @author: Urunuela Richard
* @version 3.2
*/
package programme.GUI;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import storm.Tasks.Task;
import storm.Tasks.Task.Activation;

import log.CpuTrace;
import log.TaskTrace;
import log.Trace;





public class graphGantt extends JPanel implements MouseListener,MouseMotionListener{

	/**
	 * ***************************************************************************************
	 * Internal classe
	 * ***************************************************************************************
	 */

	
	/**
	 * draw Image 
	 */
	class Window {
		Image image;
		int L=600;
		int H=200;
		public Window(){
			
			
		}
		/**
		 * 
		 */
		public void createimage(){
			image= createImage(this.L,this.H);
		}
		/**
		 * 
		 * @return
		 */
		public Graphics2D getGraphic(){
			return (Graphics2D) image.getGraphics();
		}
		/**
		 * 
		 * @return
		 */
		
		public Image getImage() {
			// TODO Auto-generated method stub
			return this.image;
		}
		/**
		 * 
		 * @return
		 */
		public int getL(){
			return this.L;
		}
		
	}
	
	/**
	 * class  message task and DVS
	 * small bull around bar
	 */
	class evtTask{
		int date;
		int level;
		graphGantt graph;
		TaskTrace Trace;
		String tag;
		/**
		 * 
		 * @param date
		 * @param level y axe
		 * @param T the trace
		 * @param graph the graph
		 */
		public evtTask(int date, int level,TaskTrace T, graphGantt graph){
			this.graph = graph;
			this.date = date;
			this.level = level;
			this.Trace = T;
			
		}
		/**
		 * 
		 * @param g
		 */
		
		public void draw(Graphics g){
			String logo="?";
			int y=0;
			int x = 12 ;
			Font F = g.getFont();
			Color police = Color.black;
			g.setFont(new Font("Dialog.plain",Font.PLAIN,10));
			if (this.Trace.isRET()) return;
			Color crt = g.getColor();
			Composite old=((Graphics2D) g).getComposite();
			/**
			 * Block or unblock
			 */
			if (this.Trace.isUBBlock()){
				g.setColor(java.awt.Color.green);
				logo = this.Trace.blocksize+"";
				y = y  -20;
			}
			/**
			 * End trace
			 */
			if (this.Trace.isEnd()){
				logo = "e";
				g.setColor(java.awt.Color.white);
				
			}
			/**
			 * Activation trace
			 */
			if (this.Trace.isActivation()){
				logo = "a";
				y = y  -10;
				g.setColor(java.awt.Color.yellow);
				
			}
			/**
			 * miss deadline
			 * use blocksize for the activation not respected
			 */
			if (this.Trace.isfailed()){
				
				logo = "m: "+this.Trace.blocksize;
				x = 45;
				y =  this.graph.zeroY - 10;
				police = Color.black;
				g.setColor(java.awt.Color.orange);
				
			}
			/**
			 * Abort deadline
			 * use blocksize for the activation not respected 
			 */
			if (this.Trace.isabort()){
				
				logo = "x: "+this.Trace.blocksize;
				
				x = 45;
				y =  this.graph.zeroY - 10;
				police = Color.black;
				
				g.setColor(java.awt.Color.red);
				
				
			}
			/**
			 * Draw round rect or apply white bar on gantt graph ...
			 */
			/**
			 * Penality application 
			 * 
			 */
			if (this.Trace.isPenality()){
				g.setColor(Color.white);
				int xx = this.graph.getdrawX(this.date)+this.graph.margeGauche;
				int yy = this.graph.margeHaute;
				if (this.graph.zoom.ordinal() == this.graph.zoom.zero.ordinal()){
				
					g.fillRect(xx+1, this.graph.margeHaute+18, 49,12 );
				}
				else if (this.graph.zoom.ordinal() == this.graph.zoom.un.ordinal()){
					g.fillRect(xx+1, this.graph.margeHaute+18, 10, 6 );
				}
				else if (this.graph.zoom.ordinal() == this.graph.zoom.deux.ordinal()){
					g.fillRect(xx, this.graph.margeHaute+18, 2,3 );
				}
			}
			/**
			 * 
			 * Normal trace draw round rect 
			 *
			 */
			else{
				((Graphics2D) g).fill(new RoundRectangle2D.Double(this.graph.getdrawX(this.date)+this.graph.margeGauche,y+this.graph.margeHaute ,
	                x,
	                10,
	                10, 10));
				g.setColor(crt);
				((Graphics2D) g).draw(new RoundRectangle2D.Double(this.graph.getdrawX(this.date)+this.graph.margeGauche,y+this.graph.margeHaute ,
	                x,
	                10,
	                10, 10));
				g.setColor(police);
			g.drawString(logo, this.graph.getdrawX(this.date)+this.graph.margeGauche+3,y+this.graph.margeHaute+9);
			}
			g.setColor(crt);
			g.setFont(F);
			((Graphics2D) g).setComposite(old);
		}

		
		
	}

	/**
	 * Bar for CPU or task (one bar)
	 * @author richard
	 *
	 */
	public class internBar {
		
		int date;
		CpuTrace Trace;
		Color color;
		graphGantt graph;
		int dvsFactor=1;
		boolean isPenality=false;
		/**
		 * 
		 * @param graphGantt
		 * @param date
		 * @param C
		 * @param tr
		 * @param dvsfactor
		 */
		public internBar(graphGantt graphGantt,int date,Color C,CpuTrace tr,int dvsfactor){
			this( graphGantt,date, C,tr);
			this.dvsFactor = dvsfactor;
		}
		/**
		 * 
		 * @param graphGantt
		 * @param date
		 * @param C
		 * @param tr
		 */
		public internBar(graphGantt graphGantt,int date,Color C,CpuTrace tr){
			this.date = date;
			this.graph= graphGantt;
			this.color = C;
			Trace =tr;
		}
		/**
		 * 
		 * @param g
		 */
		public void draw(Graphics g){
			
				Color temp = g.getColor();
				g.setColor(this.color);
				if (viewdvsfactor){
					g.fillRect(this.graph.getdrawX(this.date)+this.graph.margeGauche,this.graph.zeroY+1-(this.graph.hauteurBarre/this.dvsFactor),this.graph.intervalle,(this.graph.hauteurBarre/this.dvsFactor));
				}
				else if(this.isPenality){
					
				}
				else{
					g.fillRect(this.graph.getdrawX(this.date)+this.graph.margeGauche,this.graph.zeroY+1-(this.graph.hauteurBarre),this.graph.intervalle,(this.graph.hauteurBarre));
				}
				
				
				g.setColor(temp);
			}
		/**
		 * 
		 * @return
		 */
		public boolean isPenality() {
			return isPenality;
		}
		/**
		 * 
		 * @param isPenality
		 */
		public void setPenality(boolean isPenality) {
			this.isPenality = isPenality;
		}
		
		
	}
	
	/**
	 * ***************************************************************************************
	 *  Gantt 
	 * ***************************************************************************************
	 */
	ArrayList<internBar> Bars;
	ArrayList evtTasks;
	private Composite composite=AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f);
	/**
	 * Gantt diagram
	 */
	public   static enum TypeGraph{
		CPU,TASK
	}
	/**
	 * Zoom different level
	 */
	public enum ZOOM{
		zero,un,deux,trois
	}
	Window window;
	int date_debut;
	int date_fin;
	int duration;//dur?? en unit?? de temps de 0 ?? n
	ZOOM zoom=ZOOM.un;
	
	/**
	 * cadre
	 */
	int margeHaute=25;
	int margeGauche=30;
	int zeroX=25; //coordonn??e du point origine (pas 0 si d??calage=
	int zeroY=85;
	/**
	 * Taille des barres de base
	 */
	int hauteurBarre = 50;
	int intervalle=1; //intervalle de temps entre deux mesures 10 ?? zoom 1 - 5 ?? zoom 2 -  1 ?? zoom 3; cf largeur d'une barre 
	/**
	 * Souris
	 */
	int Xmouse=0;
	int Ymouse=0;
	/**
	 * dvs factor 
	 */
	boolean viewdvsfactor = true;
	public boolean isViewdvsfactor() {
		return viewdvsfactor;
	}


	/**
	 * Use dvs graph on gantt graph or not 
	 * @param viewdvsfactor
	 */

	public void setViewdvsfactor(boolean viewdvsfactor) {
		this.viewdvsfactor = viewdvsfactor;
	}


	/**
	 * 
	 */
	private TypeGraph type;
	private boolean redoImage=false;
	private boolean infobulle;
	/**
	 * 
	 */
	
	
	public void paint(Graphics G){
		super.paintComponent(G);
		if (this.window == null){
		//create window
			this.window = new Window();
			window.createimage();
		}
		/**
		 * redo image if necessary
		 */
		if (this.redoImage){
			redoImage = false;
			window.createimage();
		}
		this.drawBars(this.window.getGraphic());
		this.drawAxes(this.window.getGraphic());

		/**
		 * draw period and event on gantt task (not processor)
		 */
		if (this.type.ordinal() == graphGantt.TypeGraph.TASK.ordinal()){
			
			 
				this.drawPeriodDeadline(this.window.getGraphic());
				this.drawEvt(this.window.getGraphic());
			
		
		}
		/**
		 * use infobulle 
		 */
		if (this.infobulle) {
			this.drawInfobulle(this.window.getGraphic());
		}
		
		G.drawImage(window.getImage(), 0,0,this);
		
	}
	
	/**
	 * draw event 
	 * @param graphic
	 */
	private void drawEvt(Graphics2D graphic) {
		Iterator I=this.evtTasks.iterator();
		while(I.hasNext()){
			 evtTask B = ( evtTask ) I.next();
			 if ((B.date >= this.date_debut) && (B.date <= this.date_fin)) {
				 B.draw(graphic);
			 }
			 
		}
		
	}



	/**
	 * effective draw arrow for period and deadline 
	 * @param X
	 * @param up period or deadline 
	 * @param graphic
	 * @param color
	 */
	public void drawarrow (int X, boolean up,Graphics2D graphic,Color color){
		int x = this.getdrawX(X)+this.margeGauche;
		Color crt = graphic.getColor();
		float dash1[] = {10.0f};
		BasicStroke strokeCrt = (BasicStroke) ((Graphics2D) graphic).getStroke();
		BasicStroke dashed = new BasicStroke(1.0f, 
                BasicStroke.CAP_BUTT, 
                BasicStroke.JOIN_MITER, 
                10.0f, dash1, 0.0f);
		
		graphic.setColor(color);
		((Graphics2D) graphic).setStroke(dashed);
		graphic.drawLine(x,this.zeroY,x,this.margeHaute);
		
		
		
		Polygon triangle = new Polygon();
		if(up){
			triangle.addPoint(x-5,this.margeHaute);
			triangle.addPoint(x,this.margeHaute-5);
			triangle.addPoint(x+5,this.margeHaute);
		}
		else{
			triangle.addPoint(x-5,this.zeroY-5);
			triangle.addPoint(x,this.zeroY);
			triangle.addPoint(x+5,this.zeroY-5);
		}
		graphic.fillPolygon(triangle);
		
		
		
		
		
		((Graphics2D) graphic).setStroke(strokeCrt);
		graphic.setColor(crt);
	}
	/**
	 * draw period or deadline 
	 */
	private void drawPeriodDeadline(Graphics2D graphic) {
		internBar B = (internBar)(this.Bars.get(0));
		Task T = B.Trace.task;
		
		Iterator I = T.getActivationFIFO().iterator();
		
		while (I.hasNext()){
			
		Task.Activation A = (Activation) I.next();
		Color color;
		if (A.isLoseActivation()) color = Color.orange;
		else color = Color.red;
			
			//period
			if ((A.getDate()>= this.date_debut) && (A.getDate() <= this.date_fin)){
				this.drawarrow(A.getDate(), true, graphic,color);
				
			}
			//deadline 
			if ((A.getDeadline()>= this.date_debut) && (A.getDeadline() <= this.date_fin) && (A.getDeadline()>= 0)) {
				this.drawarrow(A.getDeadline(), false, graphic,color);
			
			}
			
		}
		
		
	}
	/**
	 * @deprecated
	 * @param graphic
	 */
	private void OlddrawPeriodDeadline(Graphics2D graphic) {
	
		int period;
		int deadline;
		int activation;
		internBar B = (internBar)(this.Bars.get(0));
		deadline = B.Trace.task.getDeadline();
		period =  B.Trace.task.getPeriod();
		activation = B.Trace.task.getActivationDate();
		if (period == 0 ) return;
		///parcours du graphe virtuel
		int X=0;
		
		for (int i=0;i<this.duration;i++){
			
			
			
			if ((i>=activation) && (i>=this.date_debut) && (i<=this.date_fin)) {
				 X = (i - activation);
				 if( (X%period) == 0 ) {
					 this.drawarrow(i, true, graphic,Color.red);
				
					 
				 }
				 
				 if ((deadline != 0 ) && (deadline !=period )&& (i+deadline > this.date_debut)&&(i+deadline <=this.date_fin)){
					 
						 if (((X%(period)) == 0)){
							 this.drawarrow(i+deadline, false, graphic,Color.red);
					
						 }
					
				}
				
			}
			
		}
		
			 
		
	}



/**
 * draw internal bar (all) see internal bar
 * @param g
 */
	private void drawBars(Graphics g) {
		Iterator I=this.Bars.iterator();
		while(I.hasNext()){
			 internBar B = ( internBar ) I.next();
			 if ((B.date >= this.date_debut) && (B.date <= this.date_fin)) {
				 B.draw(g);
			 }
			 
		}
		
		
		
	}


/**
 * draw axes
 * @param graphic
 */

	private void drawAxes(Graphics2D graphic) {
		int debord = 3; // taille du d??passement en bas des barres des axes
		String mark="";
		
		
		/**
		 * draw line axe X
		 * 
		 */
		graphic.drawLine(this.zeroX,this.zeroY, this.zeroX+this.window.getL(), this.zeroY);
		
		/**
		 * date run
		 */
		for (int i=0;i<this.duration;i++){
			
			graphic.setFont(new Font("Dialog.plain",Font.PLAIN,10));
			
			if ((i>=this.date_debut) && (i<=this.date_fin)) {
				//on est dans le cadre
				/**
				 * on trace une barre pour X au bon endroit en fonction du zoom
				 */
				
				if (this.zoom.ordinal() == ZOOM.zero.ordinal()){
						//draw bar toutes les  unit??e
						
						graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.margeHaute);
						mark = i+"";
						graphic.drawString(mark,this.getdrawX(i)+this.margeGauche+20,this.zeroY+(debord*4));
						
						
					}
				if (this.zoom.ordinal() == ZOOM.un.ordinal()){
					//draw bar toutes les 5 unit??e
					graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.zeroY-debord);
					if (i%5 == 0){
					graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.margeHaute);
					mark = i+"";
					graphic.drawString(mark,this.getdrawX(i)+this.margeGauche,this.zeroY+(debord*4));
					
					}
				}
				if (this.zoom.ordinal() == ZOOM.deux.ordinal()){
					//draw bar toutes les 50 unit??e
					if (i%5 == 0) {
						graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.zeroY-debord);
					}
					if (i%25 == 0){
						graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.margeHaute);
						mark = i+"";
						graphic.drawString(mark,this.getdrawX(i)+this.margeGauche-5,this.zeroY+(debord*4));
						}
				}
				if (this.zoom.ordinal() == ZOOM.trois.ordinal()){
					//draw bar toutes les 50 unit??e
					if (i%25 == 0) {
						graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.zeroY-debord);
					}
					if (i%50 == 0){
						graphic.drawLine(this.getdrawX(i)+this.margeGauche,this.zeroY+debord,this.getdrawX(i)+this.margeGauche, this.margeHaute);
						mark = i+"";
						graphic.drawString(mark,this.getdrawX(i)+this.margeGauche-5,this.zeroY+(debord*4));
						}
				}
				
			}
			
		}
		
		
		
		
	}


/**
 * return X real value on x value in the window 
 * @param i
 * @return
 */

	private int getdrawX(int i) {
		// retourne la valeur de X dans l'image en fonction du zoom et la fen??tre virtuelle
		int res = 0;
		//position virtuelle
		int X= this.intervalle * i;
		//position fen??tre
		int d = this.date_debut * this.intervalle;
		int f = this.date_fin * this.intervalle;
		//resultat
		//et contr??le 
		if ((X>=d) && (X<=f)){
		
			res = X-d;
		}
		else res= -1;
		
		return res;
	}

/**
 * change ZOOM
 * @param type
 */
	public void setZoom(ZOOM type){
		this.zoom = type;
		if (type.ordinal() == ZOOM.zero.ordinal()) {
			this.date_fin = this.date_debut + 11;
			this.intervalle = 50;
		}
		if (type.ordinal() == ZOOM.un.ordinal()) {
			this.date_fin = this.date_debut + 51;
			this.intervalle = 10;
		}
		if (type.ordinal() == ZOOM.deux.ordinal()){
			this.date_fin = this.date_debut + 251;
			this.intervalle = 2;
		}
		if (type.ordinal() == ZOOM.trois.ordinal()){
			this.date_fin = this.date_debut + 501;
			this.intervalle = 1;
		}
	}
/**
 * create processor gantt 
 * @param histoCPU
 * @param cpu
 * @param duration
 */
	public graphGantt(ArrayList<Trace> histoCPU, TypeGraph cpu, int duration) {
		super();
		addMouseListener(this);
		addMouseMotionListener(this);
		this.type=cpu;
		Bars = new ArrayList();
		evtTasks= new ArrayList();;
		this.date_debut = 0;
		this.duration = duration;
		this.setZoom(ZOOM.un);
		redoImage = true;
		/**
		 * create Bars
		 */
		Iterator I = histoCPU.iterator();
		while (I.hasNext()){
			CpuTrace T = (CpuTrace) I.next();
			Color color=Color.black;
			int dvs=1;
			dvs=(this.type.ordinal() == TypeGraph.CPU.ordinal())?T.getDvsfactor():1;
			color = T.task.color;
			
			
			internBar B = new internBar(this,T.date,color,T,dvs);
			this.Bars.add(B);
			
		}
		this.repaint();
	

	}

/**
 * create Task gantt 
 * @param histoTask
 * @param evtTask
 * @param task
 * @param evtTaskDuration
 * @param duration2
 */

	public graphGantt(ArrayList<Trace> histoTask, ArrayList<Trace> evtTask,
			TypeGraph task, int evtTaskDuration, int duration2) {
		this(histoTask,task,duration2);
		this.type=task;
	
		Iterator I= evtTask.iterator();
		while (I.hasNext()){
			TaskTrace TT = (TaskTrace) I.next(); 
			this.evtTasks.add(( new evtTask(TT.date,0,TT,this) ));
		
		
			
		}
		
	}


/**
 * Applys new window with new zoom
 * @param deb
 * @param fin
 * @param zoom
 */

	public void setTempIntervalle(int deb , int fin,ZOOM zoom){
		this.date_debut = deb;

		
		this.setZoom(zoom);

		redoImage = true;
		this.repaint();
		
	}
	
/**
 * info bulle
 */
	private void drawInfobulle(Graphics g) {


		this.getTextInfo(g);
	}
	/**
	 * draw right click information
	 * @param g
	 */
	private void getTextInfo(Graphics g) {
		 
		String res="",res2="",res3="";
		Iterator i = this.Bars.iterator();
		int date = this.getDate(Xmouse);

		while (i.hasNext()){
			internBar B = (internBar) i.next();
			
			
				
				if (B.date == date) {
				res =  "" + B.Trace.task.name  ;
				res2 = "      Period " + B.Trace.task.period + " WCET " + B.Trace.task.WCET;
				res3 = "      Date: "+B.date;
				g.setColor(Color.WHITE);
				g.fillRoundRect(Xmouse-2,Ymouse-15,200 ,55, 10, 10);
				g.setColor(Color.black);
				g.drawString(res,Xmouse,Ymouse);
				g.drawString(res2,Xmouse,Ymouse+15);
				g.drawString(res3,Xmouse,Ymouse+30);
				
			
			
				}
			}
			
			
		}
	/**
	 * return date on click
	 * @param xmouse2
	 * @return
	 */
	private int getDate(int xmouse2) {
		
		int xv = this.date_debut * this.intervalle;
		int date =  (xv + xmouse2 - this.margeGauche) / this.intervalle;
		
		return date;
	}
/**
 * 
 */
	public void mouseClicked(MouseEvent arg0) {
		
		
	}

/**
 * 
 */
	public void mouseEntered(MouseEvent arg0) {
		
		
	}
/**
 * 
 */

	public void mouseExited(MouseEvent arg0) {
		
		
	}
/**
 * 
 */

	public void mousePressed(MouseEvent arg0) {

		
		this.Xmouse = arg0.getX();
		this.Ymouse = arg0.getY();
			
		redoImage=true;
		this.infobulle = true;
		this.repaint();
		
	}
/**
 * 
 */

	public void mouseReleased(MouseEvent arg0) {
		
		this.infobulle = false;
		redoImage=true;
		repaint();
		
	}

/**
 * 
 */
	public void mouseDragged(MouseEvent arg0) {
		this.Xmouse = arg0.getX();
		this.Ymouse = arg0.getY();
		this.infobulle = true;
		redoImage=true;
		this.repaint();
		
	}

/**
 * 
 */
	public void mouseMoved(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

/**
 * 
 * @return window beginning date 
 */


	public int getDate_debut() {
		return date_debut;
	}


/**
 * 
 * @param date_debut is new beginning date
 */

	public void setDate_debut(int date_debut) {
		this.date_debut = date_debut;
	}



/**
 * 
 * @return window date end
 */
	public int getDate_fin() {
		return date_fin;
	}



/**
 * 
 * @param date_fin is new end date
 */
	public void setDate_fin(int date_fin) {
		this.date_fin = date_fin;
	}


/**
 * 
 * @return simulation duration 
 */

	public int getDuration() {
		return duration;
	}


/**
 * 
 * @param duration
 */

	public void setDuration(int duration) {
		this.duration = duration;
	}


/**
 * 
 * @return the ZOOM Value (ZERO|UN|DEUX|TROIS)
 */

	public ZOOM getZoom() {
		return zoom;
	}
	
	
}
